﻿using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;

public class GameStandardShaderGUI : BaseShaderGUI
{
    private enum RenderingMode
    {
        /// <summary>
        /// Render the opaque solid object.
        /// </summary>
        Opaque,

        /// <summary>
        /// The transparent object without semi-transparent areas.
        /// </summary>
        Cutout,

        /// <summary>
        /// Totally fade out an object, include diffuse and specular, make it
        /// completely fade out.
        /// </summary>
        Fade,

        /// <summary>
        /// The transparent object like glass, the diffuse color will fade out
        /// but the specular will maintain.
        /// </summary>
        Transparent,



    }

    private enum SmoothnessSource
    {
        MetallicAlpha,//金属度源
        AlbedoAlpha,//Albedo贴图也作为光滑度源使用
    }

    private static readonly string[] BlendNames = Enum.GetNames(typeof(RenderingMode));
    //它支持高动态范围的颜色。这意味着该颜色的RGB分量可以高于1,你就可以表示非常明亮的光。亮度范围和曝光范围。先取StandardShader数值
    private static ColorPickerHDRConfig emissionHDRConfig = new ColorPickerHDRConfig(0, 99, 1/99, 3);

    private MaterialProperty renderingMode;
    private MaterialProperty cutoff;

    private MaterialProperty mainTex;
    private MaterialProperty mainColor;
    //金属感
    private MaterialProperty metallic;
    private MaterialProperty metallicGlossTex;
    //金属光滑度
    private MaterialProperty _Glossiness;
    private MaterialProperty _GlossMapScale;
    private MaterialProperty _SmoothnessTextureChannel;
    //法线贴图
    private MaterialProperty normalMap;
    private MaterialProperty bumpScale;

    private MaterialProperty _SpecularHighlights;
    private MaterialProperty _GlossyReflections;
    //高光贴图
    private MaterialProperty heightScale;
    private MaterialProperty heightTex;
    
    private MaterialProperty _OcclusionStrength;
    private MaterialProperty _OcclusionTex;
    //自发光
    private MaterialProperty _EmissionColor;
    private MaterialProperty _EmissionTex;

    private MaterialProperty _DetailMask;

    //闪白
    private MaterialProperty rimColor;
    private MaterialProperty rimTime;
    //hologram
    private MaterialProperty hologramColor;
    private MaterialProperty hologramPower;
    private MaterialProperty hologramBrightness;
    private MaterialProperty hologramDiffuseAmount;

    protected override void FindProperties(MaterialProperty[] props)
    {
        this.renderingMode = ShaderGUI.FindProperty("_Mode", props);
        this.cutoff = ShaderGUI.FindProperty("_Cutoff", props);
        this.mainTex = ShaderGUI.FindProperty("_MainTex", props);
        this.mainColor = ShaderGUI.FindProperty("_Color", props);
        this.normalMap = ShaderGUI.FindProperty("_BumpMap", props);
        this.bumpScale = ShaderGUI.FindProperty("_BumpScale", props);

        this.metallic = ShaderGUI.FindProperty("_Metallic", props);
        this.metallicGlossTex = ShaderGUI.FindProperty("_MetallicGlossMap", props);
        this._Glossiness = ShaderGUI.FindProperty("_Glossiness", props);
        this._GlossMapScale = ShaderGUI.FindProperty("_GlossMapScale", props);
        this._SmoothnessTextureChannel = ShaderGUI.FindProperty("_SmoothnessTextureChannel", props);

        this.heightTex =  ShaderGUI.FindProperty("_ParallaxMap", props);
        this.heightScale = ShaderGUI.FindProperty("_Parallax", props);

        this._OcclusionStrength = ShaderGUI.FindProperty("_OcclusionStrength", props);
        this._OcclusionTex = ShaderGUI.FindProperty("_OcclusionMap", props);
        this._DetailMask = ShaderGUI.FindProperty("_DetailMask", props);

        this._EmissionColor = ShaderGUI.FindProperty("_EmissionColor", props);
        this._EmissionTex = ShaderGUI.FindProperty("_EmissionMap", props);

        
        this._SpecularHighlights = ShaderGUI.FindProperty("_SpecularHighlights", props);
        this._GlossyReflections = ShaderGUI.FindProperty("_GlossyReflections", props);

        //闪白
        this.rimColor = ShaderGUI.FindProperty("_RimColor", props);
        this.rimTime = ShaderGUI.FindProperty("_RimTime", props);

        // hologram
        this.hologramColor = ShaderGUI.FindProperty("_HologramColor", props);
        this.hologramPower = ShaderGUI.FindProperty("_HologramPower", props);
        this.hologramBrightness = ShaderGUI.FindProperty("_HologramBrightness", props);
        this.hologramDiffuseAmount = ShaderGUI.FindProperty("_HologramDiffuseAmount", props);

    }

    protected override void OnShaderGUI(MaterialEditor materialEditor, Material[] materials)
    {
        base.OnShaderGUI(materialEditor, materials);
        this.BlendModeGUI(materialEditor, materials);
        this.DoMainGUI(materialEditor, materials);

        this.OnSpecularLightGUI(materialEditor, materials);
        this.OnReflectionGUI(materialEditor, materials);
    }

    protected override void MaterialChanged(Material material)
    {
        if (this.renderingMode != null)
        {
            var renderingMode = (RenderingMode)this.renderingMode.floatValue;
            this.UpdateRenderingMode(renderingMode, material);
        }
    }

    private void UpdateRenderingMode(RenderingMode renderingMode, Material material)
    {
        switch (renderingMode)
        {
            case RenderingMode.Opaque:
                {
                    material.SetInt("_SrcBlend", (int)BlendMode.One);
                    material.SetInt("_DstBlend", (int)BlendMode.Zero);
                    material.SetInt("_ZWrite", 1);
                    material.DisableKeyword("_ALPHATEST_ON");
                    material.DisableKeyword("_ALPHABLEND_ON");
                    material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
                    material.SetOverrideTag("RenderType", "Opaque");
                    material.renderQueue = -1;
                    break;
                }
            case RenderingMode.Cutout:
                {
                    material.SetInt("_SrcBlend", (int)BlendMode.One);
                    material.SetInt("_DstBlend", (int)BlendMode.Zero);
                    material.SetInt("_ZWrite", 1);
                    material.EnableKeyword("_ALPHATEST_ON");
                    material.DisableKeyword("_ALPHABLEND_ON");
                    material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
                    material.SetOverrideTag("RenderType", "TransparentCutout");
                    material.renderQueue = -1;
                    break;
                }
            case RenderingMode.Fade:
                {
                    material.SetInt("_SrcBlend", (int)BlendMode.SrcAlpha);
                    material.SetInt("_DstBlend", (int)BlendMode.OneMinusSrcAlpha);
                    material.SetInt("_ZWrite", 0);
                    material.DisableKeyword("_ALPHATEST_ON");
                    material.EnableKeyword("_ALPHABLEND_ON");
                    material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
                    material.SetOverrideTag("RenderType", "Transparent");
                    material.renderQueue = 3000;
                    
                    break;
                }
            case RenderingMode.Transparent:
                {
                    material.SetInt("_SrcBlend", (int)BlendMode.One);
                    material.SetInt("_DstBlend", (int)BlendMode.OneMinusSrcAlpha);
                    material.SetInt("_ZWrite", 0);
                    material.DisableKeyword("_ALPHATEST_ON");
                    material.DisableKeyword("_ALPHABLEND_ON");
                    material.EnableKeyword("_ALPHAPREMULTIPLY_ON");
                    material.SetOverrideTag("RenderType", "Transparent");
                    material.renderQueue = 3000;
                    break;
                }

        }
    }

    private void BlendModeGUI(MaterialEditor materialEditor, Material[] materials)
    {
        EditorGUI.showMixedValue = this.renderingMode.hasMixedValue;
        var renderingMode = (RenderingMode)this.renderingMode.floatValue;
        EditorGUI.BeginChangeCheck();
        renderingMode = (RenderingMode)EditorGUILayout.Popup("Rendering Mode", (int)renderingMode, BlendNames);
        if (EditorGUI.EndChangeCheck())
        {
            materialEditor.RegisterPropertyChangeUndo("Rendering Mode");
            this.renderingMode.floatValue = (float)renderingMode;
            foreach (var mat in materials)
            {
                this.UpdateRenderingMode(renderingMode, mat);
            }
        }
        EditorGUI.showMixedValue = false;
        if (renderingMode != RenderingMode.Opaque)
        {
            materialEditor.RangeProperty(this.cutoff, "Cutoff");
        }
    }

    private void DoMainGUI(MaterialEditor materialEditor, Material[] materials)
    {
        GUILayout.Space(10);
        EditorGUILayout.LabelField("Main Map", EditorStyles.boldLabel);
       
        materialEditor.TexturePropertySingleLine(new GUIContent("Main Texture","Albedo (RGB) and Alpha"), this.mainTex, this.mainColor);
        GUILayout.Space(2);
        EditorGUI.indentLevel = 1;
        materialEditor.TextureScaleOffsetProperty(this.mainTex);
        EditorGUI.indentLevel = 0;

        this.DoNormalsGUI(materialEditor, materials);
        this.DoMetallicGUI(materialEditor, materials);
        this.DoSmoothnessGUI(materialEditor, materials);
        this.DoHightGUI(materialEditor, materials);
        this.DoOcclusionGUI(materialEditor, materials);
        this.DoDetaiMaskGUI(materialEditor, materials);

        this.DoEmissionGUI(materialEditor, materials);

        this.DoRimGUI(materialEditor, materials);
        this.DoRimHologramGUI(materialEditor, materials);
        
    }

    private void DoNormalsGUI(MaterialEditor materialEditor, Material[] materials)
    {
        GUILayout.Space(2);
        EditorGUI.BeginChangeCheck();
        materialEditor.TexturePropertySingleLine(new GUIContent("Normal Map"), this.normalMap, this.normalMap.textureValue?this.bumpScale:null);
        if (EditorGUI.EndChangeCheck())
        {
            SetKeyword((materialEditor.target as Material), "_NORMALMAP", this.normalMap.textureValue);
        }
    }

    private void DoMetallicGUI(MaterialEditor materialEditor, Material[] materials)
    {
        GUILayout.Space(2);
        EditorGUI.BeginChangeCheck();
        materialEditor.TexturePropertySingleLine(new GUIContent("Metallic"), this.metallicGlossTex, this.metallicGlossTex.textureValue?null: this.metallic);
        if (EditorGUI.EndChangeCheck())
        {
            SetKeyword((materialEditor.target as Material), "_METALLICGLOSSMAP", this.metallicGlossTex.textureValue);
        }

    }

    /// <summary>
    /// 光滑度，Unity的标准着色器希望把光滑度存储在alpha通道中
    /// 希望金属度和光滑度贴图可以在相同的纹理中结合起来，因为DXT5把RGB和A通道分别压缩，
    /// 把贴图合并到一个DXT5纹理中，可以产生与使用两个DXT1纹理相同的质量。
    /// 这不会要求更少的内存，但是可以让我们从一次纹理采样中就检索到金属度和光滑度，
    /// 而不是两次。
    /// 有一个纹理合并了贴图。尽管金属度只需要R通道，
    /// 我依旧会用金属度值填充RGB通道，光滑度使用alpha通道。
    /// 当存在一个金属度贴图时，我们可以从中得到光滑度，否则的话，我们就使用Smoothness属性
    /// </summary>
    /// <param name="materialEditor"></param>
    /// <param name="materials"></param>
    private void DoSmoothnessGUI(MaterialEditor materialEditor, Material[] materials)
    {

        EditorGUI.indentLevel = 2;
        var source = (SmoothnessSource)this._SmoothnessTextureChannel.floatValue;
        if (this.metallicGlossTex.textureValue)
        {
            materialEditor.RangeProperty(this._GlossMapScale, "Smoothness");
        }
        else
        {
            if (source == SmoothnessSource.AlbedoAlpha)
            {
                materialEditor.RangeProperty(this._GlossMapScale, "Smoothness");
            }
            else
            {
                materialEditor.RangeProperty(this._Glossiness, "Smoothness");
            }
        }

        EditorGUI.indentLevel = 0;
       
        Material mat = (materialEditor.target as Material);
        EditorGUI.indentLevel = 4;
        EditorGUI.BeginChangeCheck();

        EditorGUI.showMixedValue = this._SmoothnessTextureChannel.hasMixedValue;
    
        EditorGUI.BeginChangeCheck();
        source = (SmoothnessSource)EditorGUILayout.EnumPopup("Source", source);
        if (EditorGUI.EndChangeCheck())
        {
            materialEditor.RegisterPropertyChangeUndo("Source");
            this._SmoothnessTextureChannel.floatValue = (float)source;
            SetKeyword(mat, "_SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A", source == SmoothnessSource.AlbedoAlpha);

        }
        EditorGUI.showMixedValue = false;
        EditorGUI.indentLevel = 0;
    }


    private void DoHightGUI(MaterialEditor materialEditor, Material[] materials)
    {
        GUILayout.Space(2);
        EditorGUI.BeginChangeCheck();
        materialEditor.TexturePropertySingleLine(new GUIContent("Height Map"), this.heightTex, this.heightTex.textureValue ? this.heightScale :null );
        if (EditorGUI.EndChangeCheck())
        {
            SetKeyword((materialEditor.target as Material), "_PARALLAXMAP", this.heightTex.textureValue);
        }
    }


    private void DoOcclusionGUI(MaterialEditor materialEditor, Material[] materials)
    {
        GUILayout.Space(2);
        materialEditor.TexturePropertySingleLine(new GUIContent("Occlusion"), this._OcclusionTex, this._OcclusionTex.textureValue ? this._OcclusionStrength : null);

    }

    private void DoDetaiMaskGUI(MaterialEditor materialEditor, Material[] materials)
    {
        GUILayout.Space(2);
        materialEditor.TexturePropertySingleLine(new GUIContent("Detail Mask"), this._DetailMask);
    }

    private void DoEmissionGUI(MaterialEditor materialEditor, Material[] materials)
    {
        GUILayout.Space(2);
        if (this.CheckOption(materials,"Emission", "_EMISSION"))
        {
            EditorGUI.indentLevel = 1;
            materialEditor.TexturePropertyWithHDRColor(new GUIContent("Emission"), this._EmissionTex, this._EmissionColor, emissionHDRConfig, false);
            EditorGUI.indentLevel = 0;
        }
    }

    private void OnSpecularLightGUI(MaterialEditor materialEditor, Material[] materials)
    {
        GUILayout.Space(10);
        EditorGUILayout.LabelField("Forward Rendering Options", EditorStyles.boldLabel);
        bool flag = this._SpecularHighlights.floatValue == 0? false:true;
        EditorGUI.BeginChangeCheck();
        flag = EditorGUILayout.Toggle("Specular Highlights", flag, new GUILayoutOption[0]);
        if (EditorGUI.EndChangeCheck())
        {
            this._SpecularHighlights.floatValue = flag?1:0;
            if (flag)
            {
                foreach (Material material in materials)
                {
                    material.DisableKeyword("_SPECULARHIGHLIGHTS_OFF"); 
                }
            }
            else
            {
                foreach (Material material2 in materials)
                {
                    material2.EnableKeyword("_SPECULARHIGHLIGHTS_OFF");
                }
            }
        }

    }

    private void OnReflectionGUI(MaterialEditor materialEditor, Material[] materials)
    {
        GUILayout.Space(2);
        bool flag = this._GlossyReflections.floatValue == 0 ? false : true;
        EditorGUI.BeginChangeCheck();
        flag = EditorGUILayout.Toggle("Reflections", flag, new GUILayoutOption[0]);
        if (EditorGUI.EndChangeCheck())
        {
            this._GlossyReflections.floatValue = flag ? 1 : 0;
            if (flag)
            {
                foreach (Material material in materials)
                {
                    material.DisableKeyword("_GLOSSYREFLECTIONS_OFF");
                }
            }
            else
            {
                foreach (Material material2 in materials)
                {
                    material2.EnableKeyword("_GLOSSYREFLECTIONS_OFF");
                }
            }
        }

    }

    private void DoRimGUI(MaterialEditor materialEditor, Material[] materials)
    {
        GUILayout.Space(2);
        if (this.CheckOption(
                materials,
                "Enable Rim",
                "ENABLE_RIM"))
        {
            EditorGUI.indentLevel = 1;
            materialEditor.ColorProperty(this.rimColor, "Color");
            materialEditor.RangeProperty(this.rimTime, "TimeSpeed");
            Debug.Log("Rim HSV:" + this.rimColor.colorValue);//打印真实的hsv颜色值，好调试
            EditorGUI.indentLevel = 0;
        }
    }

    private void DoRimHologramGUI(MaterialEditor materialEditor, Material[] materials)
    {
        GUILayout.Space(2);
        if (this.CheckOption(
                materials,
                "Enable Hologram",
                "ENABLE_HOLOGRAM"))
        {
            EditorGUI.indentLevel = 1;
            materialEditor.ColorProperty(this.hologramColor, "Hologram Color");
            materialEditor.RangeProperty(this.hologramPower, "Hologram Power");
            materialEditor.RangeProperty(this.hologramBrightness, "Hologram Brightness");
            materialEditor.RangeProperty(this.hologramDiffuseAmount, "Hologram Diffuse Amount");
            Debug.Log("Hologram HSV:"+this.hologramColor.colorValue);//打印真实的hsv颜色值，好调试
            EditorGUI.indentLevel = 0;

        }
    }

    void SetKeyword(Material mat,string keyword,bool state)
    {
        if (state) mat.EnableKeyword(keyword);
        else mat.DisableKeyword(keyword);
    }

}
